package nz.ac.massey.cs.presentaion;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JToolBar;
import javax.swing.Timer;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import nc.ac.massey.cs.plugin.ManagePlugins;
import nz.ac.massey.cs.domain.*;

//Adapted from Jens Dietrich's hanjie solver 
public class SudokuSolverGUI {
	private JLabel counterLabel = new JLabel(" 0");
	private JSlider slider = new JSlider();
	private Action actLoad = null;
	private Action actNext = null;
	private Action actExit = null;
	private Action actPlay = null;
	private Action actStop = null;
	
	private boolean animationOn = false;
	private int counter = 0;
	
	// misc
	private Timer timer = null;
	

    private void initActions() {
    	actExit = new AbstractAction("exit") {
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent e) {
				page.dispose();
				System.exit(0);
			}
    	};
    	
		actLoad = new AbstractAction("load") {
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent e) {		
			       
				JFileChooser chooser = new JFileChooser();
			    try {
			    	chooser.setCurrentDirectory(new File("..\\\\SudokuSolver\\sudokus"));
			    }
			    catch (Exception x){}
			    int returnVal = chooser.showOpenDialog(page);
			    if(returnVal == JFileChooser.APPROVE_OPTION) {
			       File file = chooser.getSelectedFile();
			       try {
			    	   sudoku = SudokuFactory.buildSudoku(file.getAbsolutePath());
			    	   sudokuType = SudokuFactory.sudokuType;
			    	   updateBoard(sudoku,true,null);
			    	   switchAnimation(false);
			    	   solver = new Solver(sudoku);
			    	   sudokuNameLbl.setText(file.getName());
			       }
			       catch (Exception a) {
			    	   System.out.println(a.getMessage());
			       }
			    }
			}
		};
		
		actNext = new AbstractAction("next") {
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent e) {				
				  Cell cell = solver.solveNext();
				  if(cell == null)
				  {
					  actPlay.setEnabled(false);
					  actStop.setEnabled(false);
					  actNext.setEnabled(false);
				  }
				  else
				  {
					  updateBoard(sudoku,false,cell);
				  }
			}
		};
		
		// timer
		ActionListener l = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if (animationOn) {
					  Cell cell = solver.solveNext();
					  if(cell == null)
					  {
						  timer.stop();
						  switchAnimation(false);
						  actPlay.setEnabled(false);
						  actStop.setEnabled(false);
						  actNext.setEnabled(false);
						  
					  }
					  updateBoard(sudoku,false,cell);
					  //timer.restart();
				}
				else {
					timer.stop();
				}
			};					
		};
		timer = new Timer(1000,l);
		
		actPlay = new AbstractAction("play") {			
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent e) {				
				switchAnimation(true);
				timer.start(); 			}
		};
		
		actStop = new AbstractAction("stop") {
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent e) {				
				  switchAnimation(false);
				  timer.stop();
			}
		};
	}

    /*
     * update the board to the current state of the program
     */
	private void updateBoard(ISudoku g,boolean initialSetup,Cell changed) {
		
		if (initialSetup)  {
			drawSudoku();
		}
		
		for (int y = 0 ; y < sudoku.getColSize() ; y++) {
			for (int x = 0 ; x < sudoku.getRowSize() ;x++) {
				if(sudoku.getCellByIndex(x, y) == changed || sudoku.puzzleComplete())
				{
					buttons[x][y].setBorder(border);
				}
				else	
				{
					buttons[x][y].setBorder(null);
				}
				buttons[x][y].setText(sudoku.getCellByIndex(x, y).getValue());
			}
		}

		if (initialSetup) {
			this.counter = 0;
		}
		else {
			this.counter=this.counter+1;
		}
		this.counterLabel.setText(" "+this.counter);
		
		if (sudoku.puzzleComplete()) {
			switchAnimation(false);
			for (int y = 0 ; y < sudoku.getColSize() ; y++) {
				for (int x = 0 ; x < sudoku.getRowSize() ;x++) {
						buttons[y][x].setBorder(null);
				}
			}
		}
		
		sudokuBoard.repaint();
	}

	private void drawSudoku()
	{
		this.buttons = new JButton[sudoku.getColSize()][sudoku.getRowSize()];
		try
		{
			IDrawer myDrawer = ManagePlugins.getSudokuDrawingPlugins(sudokuType + "Drawer");
			sudokuBoard = myDrawer.buildSudokuBoard(this.sudoku, this.sudokuBoard,this.buttons);
			sudokuBoard.setBackground(Color.BLACK);
			
		} 
		catch (Exception e) // a drawing plugin not avalable
		{
			sudokuBoard.removeAll();
			sudokuBoard.setLayout(new GridLayout(sudoku.getColSize()+1,sudoku.getRowSize()+1,1,1));
			sudokuBoard.setBackground(Color.BLACK);
			
			for(int y = 0; y < sudoku.getColSize(); y++)
			{
				for(int x = 0; x < sudoku.getRowSize();x++)
				{
					JButton button = new JButton(sudoku.getCellByIndex(y, x).getValue());
					sudokuBoard.add(button);
					buttons[y][x] = button;
				}
			}
		}
		this.sudokuBoard.revalidate();
	}

	private void switchAnimation(boolean b) {
		this.animationOn = b;
		this.actPlay.setEnabled(sudoku != null && !b);
		this.actStop.setEnabled(b);
		this.actNext.setEnabled(sudoku != null && !b);
		this.actLoad.setEnabled(!b);
	}

	private JButton[][] buttons = null;
	ISolver solver; 
	ISudoku sudoku;
	JFrame page;
	JPanel sudokuBoard;
	private JToolBar toolbar = new JToolBar();
	Border border = BorderFactory.createLineBorder(Color.RED);
	JLabel sudokuNameLbl = new JLabel("Puzzle Name", JLabel.CENTER);
	String sudokuType = "";
	
	/**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
    	final SudokuSolverGUI  instance = new SudokuSolverGUI();
    	instance.buildGui();
		 	
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
            	instance.page.setVisible(true);
            }
        });
    }
    
        public void buildGui()
        {
        	solver = new Solver();
        	page = new JFrame();
        	page.setTitle("Sudoku Solver");
        	sudokuBoard = new JPanel();
        	JPanel content = new JPanel(new BorderLayout());
        	page.setContentPane(content);
        	
        	sudokuBoard.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
        	sudokuBoard.setBackground(Color.BLACK);
            content.add(sudokuBoard,BorderLayout.CENTER);
            page.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
            page.setSize(600,800);            
            
            content.add(sudokuNameLbl, BorderLayout.NORTH);
            initActions(); // need to understand better
            JPanel bPane = new JPanel(new GridLayout(3,1,3,3));
            content.add(bPane,BorderLayout.SOUTH);

            toolbar.setLayout(new FlowLayout(FlowLayout.CENTER));
            toolbar.setFloatable(false);
            toolbar.add(actExit);
            toolbar.addSeparator();
            toolbar.add(actLoad);
            toolbar.add(actNext);
            toolbar.add(actPlay);
            toolbar.add(actStop);
            this.switchAnimation(false);
            
            // slider
            slider.setOrientation(slider.HORIZONTAL);
            slider.setMinimum(0);
            slider.setMaximum(2000);
            slider.setValue(1000);
            slider.addChangeListener(
            		new ChangeListener() {
    					@Override
    					public void stateChanged(ChangeEvent e) {
    						int value = slider.getValue();
    						// min delay is 100 ms
    						timer.setDelay(Math.max(10,value));
    					}
            		}
            );
            slider.setMajorTickSpacing(500);
            slider.setMinorTickSpacing(100);
            slider.setPaintLabels(true);
            slider.setPaintTicks(true);
          
            JPanel sliderPane = new JPanel(new GridLayout(1,2,5,5));
            sliderPane.setBorder(BorderFactory.createEmptyBorder(0,5,0,10));
            sliderPane.add(new JLabel("animation speed (delay in ms):",JLabel.RIGHT));
            sliderPane.add(slider);
            bPane.add(sliderPane);
            JPanel stepsPane = new JPanel(new GridLayout(1,2,5,5));
            stepsPane.setBorder(BorderFactory.createEmptyBorder(0,5,0,10));
            stepsPane.add(new JLabel("computation steps:",JLabel.RIGHT));
            stepsPane.add(this.counterLabel);
            bPane.add(stepsPane);
            bPane.add(toolbar);
            
            this.counter = 0; // Initialise counter
        }
}
